package com.haust.community.service;

import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.haust.community.mapper.DiscussPostMapper;
import com.haust.community.pojo.DiscussPost;
import com.haust.community.utils.SensitiveFilter;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.util.HtmlUtils;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Auther: csp1999
 * @Date: 2020/11/24/10:54
 * @Description:
 */
@Service
public class DiscussPostService {

    private final static Logger logger = LoggerFactory.getLogger(DiscussPostService.class);

    @Autowired
    private DiscussPostMapper discussPostMapper;

    @Autowired
    private SensitiveFilter sensitiveFilter;

    @Value("${caffeine.posts.max-size}")
    private int caffeineCacheMaxSize;// 最大本地缓存数据的条数

    @Value("${caffeine.posts.expire-seconds}")
    private int caffeineCacheExpireSeconds;// 本地缓存数据的过期时间

    /**
     * Caffeine 核心接口：Cache , LoadingCache , AsyncLoadingCache
     */
    // 帖子列表缓存
    private LoadingCache<String, List<DiscussPost>> discussPostListCache;

    // 帖子总数缓存
    private LoadingCache<Integer, Integer> discussPostRowsCache;

    /**
     * 当该类被实例化或者被调用时,
     * 该init() 方法在构造函数以及@Autowired 之后执行
     */
    @PostConstruct
    public void init() {
        // 初始化帖子列表缓存
        discussPostListCache = Caffeine.newBuilder()
                // 最大本地缓存数据的条数
                .maximumSize(caffeineCacheMaxSize)
                // 本地缓存数据的过期时间
                .expireAfterWrite(caffeineCacheExpireSeconds, TimeUnit.SECONDS)
                .build(new CacheLoader<String, List<DiscussPost>>() {
                    @Override
                    public @Nullable List<DiscussPost> load(@NonNull String key) throws Exception {
                        // 判断获取缓存的key 是否为空
                        if (key == null || key.length() == 0) {
                            throw new IllegalArgumentException("key为空...");
                        }

                        // 分割key 获得参数(limit 和 offset)
                        String[] params = key.split(":");
                        if (params == null || params.length != 2) {
                            throw new IllegalArgumentException("参数错误...");
                        }

                        int offset = Integer.valueOf(params[0]);
                        int limit = Integer.valueOf(params[1]);

                        // 扩展：可以自己再加一个二级缓存 Redis -> Mysql

                        // 从数据库查数据,获取后将数据放入本地缓存
                        logger.info("从DB中获取帖子列表数据...");
                        return discussPostMapper.selectDiscussPosts(0, offset, limit, 1);
                    }
                });

        // 初始化帖子总数缓存
        discussPostRowsCache = Caffeine.newBuilder()
                // 最大本地缓存数据的条数
                .maximumSize(caffeineCacheMaxSize)
                // 本地缓存数据的过期时间
                .expireAfterWrite(caffeineCacheExpireSeconds, TimeUnit.SECONDS)
                .build(new CacheLoader<Integer, Integer>() {
                    @Override
                    public @Nullable Integer load(@NonNull Integer key) throws Exception {

                        // 从数据库查数据,获取后将数据放入本地缓存
                        logger.info("从DB中获取帖子总数量...");
                        return discussPostMapper.selectDiscussPostRows(key);
                    }
                });
    }

    /**
     * 查询用户发布的所有帖子(分页)
     *
     * @param userId 用户id
     * @param offset 起始位置
     * @param limit  每一页的数量
     * @return
     */
    public List<DiscussPost> findDiscussPosts(int userId, int offset, int limit, int orderMode) {

        // 当用户id为0 且 orderMode为1即热门帖子
        if (userId == 0 && orderMode == 1) {
            String cacheKey = offset + ":" + limit;
            // 从本地缓存中获取数据
            return discussPostListCache.get(cacheKey);
        }

        // 不满足以上条件,则从数据库查数据
        logger.info("从DB中获取帖子列表数据...");
        return discussPostMapper.selectDiscussPosts(userId, offset, limit, orderMode);
    }

    /**
     * 根据userid 查询该用户发布的所有帖子数量
     *
     * @param userId 用户id
     * @return
     */
    public int findDiscussPostRows(int userId) {

        // 当用户id为0时
        if (userId == 0) {
            Integer cacheKey = userId;
            // 从本地缓存中获取数据
            return discussPostRowsCache.get(cacheKey);
        }

        // 不满足以上条件,则从数据库查数据
        logger.info("从DB中获取帖子数据的总数量...");
        return discussPostMapper.selectDiscussPostRows(userId);
    }

    /**
     * 新增帖子(过滤敏感词)
     *
     * @param discussPost
     * @return
     */
    public int addDiscussPost(DiscussPost discussPost) {
        if (discussPost == null) {
            throw new IllegalArgumentException("参数不能为空!");
        }

        // 转义HTML标记
        discussPost.setTitle(HtmlUtils.htmlEscape(discussPost.getTitle()));
        discussPost.setContent(HtmlUtils.htmlEscape(discussPost.getContent()));
        // 过滤敏感词
        discussPost.setTitle(sensitiveFilter.filter(discussPost.getTitle()));
        discussPost.setContent(sensitiveFilter.filter(discussPost.getContent()));

        return discussPostMapper.insertDiscussPost(discussPost);
    }

    /**
     * 根据id查询帖子
     *
     * @param id
     * @return
     */
    public DiscussPost findDiscussPostById(int id) {
        return discussPostMapper.selectDiscussPostById(id);
    }

    /**
     * 根据id修改评论数量
     *
     * @param id
     * @param commentCount
     * @return
     */
    public int updateCommentCount(int id, int commentCount) {
        return discussPostMapper.updateCommentCount(id, commentCount);
    }

    /**
     * 根据id修改帖子类型
     *
     * @param id
     * @param type
     * @return
     */
    public int updateType(int id, int type) {
        return discussPostMapper.updateType(id, type);
    }

    /**
     * 根据id修改帖子状态
     *
     * @param id
     * @param status
     * @return
     */
    public int updateStatus(int id, int status) {
        return discussPostMapper.updateStatus(id, status);
    }

    /**
     * 修改帖子分数
     *
     * @param id
     * @param score
     * @return
     */
    public int updateScore(int id, double score) {
        return discussPostMapper.updateScore(id, score);
    }
}
